<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<html >
<head>
  <meta charset="UTF-8">
  <title>Packet Hub - Online network Packet Analyzer / Archive</title>
  <meta http-equiv="description" content="Online hex-dump network packet decoder. Analyzes protocol structure and fields of network packets in hexadecimal format.">
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <link rel="stylesheet" href="css/styles.css" >
  <script src="js/jquery-1.7.2.js"></script>
  <script src="js/gdoj.js"></script>
</head>
<body>
<div th:replace="common::web-navbar"></div>
<div class="container">
  <div class="content" >
    <div class="content-no-sidebar">
      <div class="packetor-container">
        <div class="packet-input">
          <div style="text-align: center;margin-bottom: 20px">
            <span class="big-text" th:text="${total}"></span><br>
            <span class="pcount-text" >[[#{packet-summary}]]</span>
          </div>
          <div>
            <textarea class="packet-hex-textarea small-scrollbar" id="hex_packet_data" rows="6" cols="50" spellcheck="false" th:text="${hex}"></textarea>
          </div>
          <div>
            <span>[[#{packethub-desc}]]</span>
          </div>
          <div>
            <input class="packet-decode-button" type="submit" id="button-decode" th:value="#{packet-analyze}">
          </div>
          <div class='packet-decode-loader'></div>
        </div>
        <div class="decoded-packet-verbose"></div>
        <div class="decoded-packet-layout">
          <div class="decoded-packet-div small-scrollbar"></div>
          <div class="decoded-packet-hex small-scrollbar"></div>
        </div>
        <div class="packets-layout">
          <div class="packets-search">
            <input class="packet-search-input" name="packet-search" type="text" th:placeholder="#{packet-search-placeholder}" value=""/>
          </div>
          <table class="packets">
            <tr class="header">
              <th class="protocol">
                [[#{packets.protocol}]]
              </th>
              <th class="packet-verbose">
                [[#{packets.verbose}]]
              </th>
              <th class="date">
                [[#{packets.date}]]
              </th>
              <th class="user">
                [[#{packets.user}]]
              </th>
            </tr>
          </table>
        </div>
      </div>
    </div>
  </div>
  <div th:replace="common::web-footer-packethub"></div>
</div>
<script th:src="@{/js/messager.js}"></script>

<script type="text/javascript">
  $(document).ready(function() {
    var g_xml = "";
    var limit = 10;
    var searchPacketWord = "";
    var numPacket = 0;
    var nothing = 0;

    function showTips(message) {
      $.getJSON("/api/i18n/get", {message: message},function(json) {
        if (json.code === 200) {
          $.messager.show('Info', json.data, 5000);
        }
      });
    }

    function showPackets(packets) {
      $.each(packets, function(i, event) {
        var opt = ""
        if (i%2 == 1) {
          opt += "<tr class='packet-item dark'>";
        } else {
          opt += "<tr class='packet-item'>";
        }
        opt += "<td class=\"protocol\" title='" + packets[i].protocol + "'>" +
                packets[i].protocol.replaceAll("eth:", "").
                replaceAll("ethertype:", "").
                replaceAll("ip:udp:", "").
                replaceAll("ip:tcp:", "").
                replaceAll("ipv6:tcp:", "").
                replaceAll("ipv6:udp:", "").
                replaceAll("osi:", "").
                replaceAll("llc:", "").
                replaceAll("isis:", "").
                replaceAll("vlan:", "").
                replaceAll("pwethheuristic:pwethcw:", "").
                replaceAll(":data", "").
                replaceAll("ipv6:", "").
                replaceAll("ip:", "");
        var verbose = packets[i].verbose;
        if (verbose == null || verbose.length == 0) {
         verbose = packets[i].hex;
        }
        opt += "</td><td class='packet-verbose' hex='"+packets[i].hex+"'>" + verbose;
        opt += "</td><td class=\"date\" title='"+packets[i].date+"'>" + packets[i].friendlyDate;
        opt += "</td><td class=\"user\">";
        if (packets[i].username) {
          opt += "<a class='rated-user user-rate-" + packets[i].user.rate +
                    " user-tip' href='/profile/"+packets[i].username+"'>"+packets[i].username+"</a>";
        } else {
          opt += "-";
        }
        opt += "</td>";
        opt += "</tr>"
        $(".packets tr:last").after(opt);
        numPacket += 1;
      });
    }

    function packetsSearch(word) {
      if (searchPacketWord !== word) {
        $('table.packets tr:not(:first)').empty();
        numPacket = 0;
        searchPacketWord = word;
      }

      $.getJSON("/api/packet/search",{q: word, limit: limit, offset: numPacket},function(json) {
        if (json.success != true) {
          showPackets(json.data);
          if (json.data.length == limit) {
            if (document.documentElement.clientHeight == document.documentElement.scrollHeight) {
              setTimeout(function () { packetsSearch(word); }, 500);
            }
          } else {
            if (nothing == 0) {
              nothing = 1;
              showTips("packet-nothing");
              setTimeout(function () { nothing = 0; }, 5100);
            }
          }
        }
      });
    }

    function showMorePackets() {
      packetsSearch($(".packet-search-input").val());
    }
    function refreshAllPackets() {
      numPacket = 0;
      $('tr.packet-item').remove();
      $('tr.append-packet-nothing').remove();
      removePacketDetail();
      showMorePackets()
    }

    showMorePackets();
    $(".packet-search-input").bind('input propertychange',function(e) {
      if (window.event && event.type == 'propertychange' && event.propertyName != 'value')
        return;
      window.clearTimeout($(this).data('timeout'));
      $(this).data('timeout', setTimeout(function () {
        showMorePackets();
      }, 1000));
    });

    window.addEventListener("scroll", function () {
      var clientHeight = document.documentElement.clientHeight;
      var scrollTop = document.documentElement.scrollTop
      var scrollHeight = document.documentElement.scrollHeight;
      if (scrollHeight - scrollTop - clientHeight <= 3) {
        window.scrollTo(0, scrollTop - 10);
        setTimeout(function () { showMorePackets(); }, 500);
      }
    });

    function packet_tree_item_expland_farther(e) {
      if (e == undefined) {
        return;
      }
      if (e.hasClass("listClose")) {
        //console.log("show li id " + e.attr("id") + " " + e.attr("class"))
        e.addClass("listOpen")
        e.removeClass("listClose")
      }

      var parentLi = e.parent('li');
      if (parentLi.length) {
        packet_tree_item_expland_farther(parentLi.get(0))
        return;
      }

      var parentUl = e.parent('ul');
      if (parentUl.length && parentUl.css('display') == 'none') {
        parentUl.slideToggle();
        //console.log("open ul pos:" + parentUl.attr('pos') + ", size:" + parentUl.attr('pos'));
        parentLi = parentUl.parent('li');
        packet_tree_item_expland_farther(parentLi)
        return;
      }
    }

    function packet_tree_item_hight_lite(index) {
      var pos_max = -1;
      var size_max = -1;
      var target_id;
      var lis = document.getElementsByTagName('li');
      for (var i = 0; i < lis.length; i ++){
        if (lis[i].className.match(/(^| )item-packet( |$)/)){
          var pos = lis[i].getAttribute("pos") * 1
          var size = lis[i].getAttribute("size") * 1
          var id = lis[i].getAttribute("id")
          if (index >= pos && index < pos + size) {
            if (pos_max == pos) {
              if (size <= size_max) {
                size_max = size;
                target_id = id;
              }
            } else if (pos > pos_max) {
              size_max = size;
              pos_max = pos;
              target_id = id;
            }
          }
        }
      }

      var t = $("#"+target_id);
      if (t != null) {
        var elements = document.querySelectorAll('.item-packet-showname');
        elements.forEach(function(e) {
          e.classList.remove('item-packet-showname-on');
        });

        packet_tree_item_expland_farther(t);

        t.children(".item-packet-showname").addClass("item-packet-showname-on");
      }
      //console.log("# index:" + index + ", pos:" + pos_max + ", size:" + size_max)
      packet_tree_hex_hight_lite(pos_max, size_max)
    }

    $(document).on("mouseover",".packet-hex-char",function(e){
      let index =  Math.floor($(this).attr("index")/3)
      //console.log($(this).attr("index") + "  " + index)
      packet_tree_item_hight_lite(index);
    });

    function packet_tree_hex_hight_lite(pos, size) {
      var elements = document.querySelectorAll('.packet-hex-char');
      elements.forEach(function(e) {
        e.classList.remove('packet-hex-char-on');
      });
      for (let j = pos * 3; j < (pos * 3) + (size * 3) - 1; j++) {
        let e = document.getElementById('packet-hex-char'+j);
        if (e != null) {
          e.classList.add('packet-hex-char-on');
        }
      }
      var elementsAsic = document.querySelectorAll('.packet-hex-asic');
      elementsAsic.forEach(function(e) {
        e.classList.remove('packet-hex-asic-on');
      });
      for (let i = pos; i < pos * 1 + size * 1; i++) {
        let e = document.getElementById('packet-hex-asic'+i);
        if (e != null) {
          e.classList.add('packet-hex-asic-on');
        }
      }
    }

    function treeEventInstall() {
      $(document).off("click",".item-packet");
      $(document).on("click",".item-packet",function(e){
        if (e.target.className.match(/(^| )item-packet-showname( |$)/)) {
          e.target = e.target.parentElement;
        }
        if (e.target.id != $(this).attr("id")) {
          return false;
        }

        var elements = document.querySelectorAll('.item-packet-showname');
        elements.forEach(function(e) {
          e.classList.remove('item-packet-showname-on');
        });

        let pos = $(this).attr("pos");
        let size = $(this).attr("size");
        packet_tree_hex_hight_lite(pos, size)

        $(this).children(".item-packets").each(function() {
          var open = e.target.className.match(/(^| )listOpen( |$)/);
          var close = e.target.className.match(/(^| )listClose( |$)/);
          e.target.className = e.target.className.replace(/(^| )list(Open|Close)( |$)/, '');
          if (open) {
            e.target.className += " listClose"
          }
          if (close) {
            e.target.className += " listOpen"
          }
          $(this).slideToggle(20);
        });
      });
    }
    treeEventInstall();

    function initTreeExpland() {
      var lis = document.getElementsByTagName('li');
      for (var index = 0; index < lis.length; index ++){
        if (lis[index].className.match(/(^| )item-packet( |$)/)){
          var subUls = lis[index].getElementsByTagName('ul');
          if (subUls.length > 0) {
            lis[index].className += ' listClose';
          }
        }
      }
    }

    function build_packet_tree(obj, xml) {
      g_xml = xml;
      let index = 1;
      obj.append("<ul class='packet-ul'></ul>");
      var nodes = $(xml).find('packet')
      build_packet_sub_tree(nodes, obj.children().eq(0));
      function build_packet_sub_tree(parent, p_div){
        $(parent).children().each( function(n,value){
          if ($(this).attr("hide") != "yes" && $(this).attr("name") != "geninfo") {
            var size = $(this).attr("size")
            var pos = $(this).attr("pos")
            var showname = $(this).attr("showname")
            if ($(this).attr("name") == "frame") {
              showname = showname.split(",")[0];
            }
            if (showname == undefined) {
              showname = $(this).attr("show")
            }
            var li = $("<li pos='"+pos+"' size='"+size+"' class='item-packet' id='"+ (index++) +"'><span class='item-packet-showname'>"+showname+"</span></li>");
            $(p_div).append(li);
            if($(this).children().length){
              var ul = $("<ul pos='"+pos+"' size='"+size+"' class='item-packets'></ul>");
              $(ul).appendTo(li);
              build_packet_sub_tree(this, ul);
            }
          }
        });
      }
    }
    function build_packet_hex(obj, hexDump) {
      const lines = hexDump.split('\n');
      let prefix = ""
      let hex = ""
      let asic = ""
      let i = 0;
      let j = 0;
      lines.forEach(function (line) {
        prefix += line.substr(0,4) + "\n";

        let line_hex = line.substr(6,47) + "\n";
        line_hex.split("").forEach((c) => {
          hex += "<span class='packet-hex-char' id='packet-hex-char"+i+"' index='"+(i++)+"'>"+c+"</span>";
        });

        let line_asic = line.substr(56,16) + "\n";
        line_asic.split("").forEach((c) => {
          if (c == '\n') {
            asic += "<span>"+c+"</span>";
          } else {
            asic += "<span class='packet-hex-asic' id='packet-hex-asic"+j+"' index='"+(j++)+"'>"+c+"</span>";
          }
        });
      })

      let str = "<div class='packet-prefix'><pre class='prettyprint '>" + prefix + "</pre></div>" +
                "<div class='packet-data'><pre>" + hex + "</pre></div>" +
                "<div class='packet-asic'><pre class='prettyprint '>" + asic + "</pre></div>"
      obj.html(str);
    }

    $("#button-decode").click(function () {
      var data = document.getElementById("hex_packet_data").value.replace(/\s*/g,"");
      if (data.length > 8192) {
        $.messager.show('Error', 'Max packet length 8192 is support.', 5000);
        return;
      }
      removePacketDetail();

      $(".packet-decode-loader").html("<img src='/img/loader.gif' />");

      $.getJSON("/api/packet/decode",{hex: data},function(json) {
        if (json.code == 200) {
          removePacketDetail();
          showTips("packet-analyze-success");
          $(".big-text").html(json.data.total);
          $(".decoded-packet-verbose").html(json.data.verbose);
          $(".decoded-packet-div").html("");
          build_packet_tree($(".decoded-packet-div"), json.data.xml);
          build_packet_hex($(".decoded-packet-hex"), json.data.hexDump);
          if (json.data.xml.length > 2) {
            initTreeExpland();
          }
        } else {
          showTips("packet-analyze-failed");
        }
        treeEventInstall();

        // refresh packets list
        refreshAllPackets();
      });
    })

    function removePacketDetail() {
      $(".packet-decode-loader").html("");
      $('tr.append-packet-refresh').remove();
      $('tr.append-packet').remove();
      $('tr.append-packet-copy').remove();
    }
    $(document).on("click",".packet-item",function(e){
      var nextElmt = e.target.parentElement.nextSibling
      if (nextElmt.className != undefined && nextElmt.className.match(/(^| )append-packet-copy( |$)/)) {
        return true;
      }
      $(".decoded-packet-verbose").html("");
      $(".decoded-packet-div").html("");
      $(".decoded-packet-hex").html("");
      removePacketDetail();

      var obj = $(this);
      var hex = obj.children(".packet-verbose").attr("hex");
      if (hex.length == 0) {
        return true;
      }

      var htmlReresh = "<tr class='append-packet-refresh'><td colspan='5' ><img src='/img/loader.gif' /></td></tr>"
      removePacketDetail();
      obj.after(htmlReresh);

      $.getJSON("/api/packet/decode",{hex: hex},function(json) {
        if (json.code == 200) {
          var hexDump = json.data.hexDump;
          var htmlCopy = "<tr class='append-packet-copy'><td colspan='5' >" +
            "<a class='submit-button packet-copy-button'>"+json.data.copy+"</a>"+
             "<a class='cancel-button packet-copy-cancel'>"+json.data.close+"</a>"+
             "</td></tr>"

          var div_pkt = $("<div></div>");
          build_packet_tree(div_pkt, json.data.xml);

          var div_hex = $("<div></div>");
          build_packet_hex(div_hex, json.data.hexDump);
          var html = "<tr class='append-packet'><td colspan='5' >"+
                  "<div class='append-packet-verbose'>"+json.data.verbose+"</div>" +
                  "<div class='append-packet-div'><div class='append-packet-detail small-scrollbar'>" + div_pkt.html() +
                  "</div><div class='append-packet-hex'>"+div_hex.html()+"</div></div></td>></tr>"
          removePacketDetail();
          obj.after(htmlCopy + html);
          initTreeExpland();
          treeEventInstall();
        }
      });
    });

    function copyToClipboard(text) {
      var dummy = document.createElement("textarea");
      document.body.appendChild(dummy);
      dummy.value = text;
      dummy.select();
      document.execCommand("copy");
      document.body.removeChild(dummy);
    }
    $(document).on("click",".packet-copy-button",function(e){
      var hex = $(this).closest("tr").prev().children(".packet-verbose").attr("hex");
      copyToClipboard(hex);
      showTips("packet-copy-success");
    });
    $(document).on("click",".packet-copy-cancel",function(e){
      removePacketDetail();
    });
  });

</script>
</body>
</html>
